<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <div class="upload select">
        <div class="upload-select">
            <input type="file" />
        </div>

        <div class="upload-progress" style="--percent: 0">
            <div class="progress-bar"></div>
            <div style="color: #fff">文件上传中...</div>
            <button>取消</button>
        </div>

        <div class="upload-result">
            <button>x</button>
        </div>

        <img src="" alt="" srcset="" class="preview" />
    </div>

</body>
<style>
    .upload {
        display: flex;
        width: 200px;
        height: 200px;
        position: relative;
        border-radius: 5px;
        border: 1px dashed #8d8b8b;
    }

    .upload-select {
        flex: 1;
        position: relative;
    }

    .upload-select::before {
        content: '+';
        position: absolute;
        top: 50%;
        left: 50%;
        transform: translateX(-50%) translateY(-100%) scale(6);
        color: #8d8b8b;
    }

    .upload-select input {
        opacity: 0;
        width: 100%;
        height: 100%;
    }

    .upload-progress {
        flex: 1;
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: center;
        background-color: rgba(0, 0, 0, 0.3);
        position: relative;
    }

    .upload-progress .progress-bar {
        background-color: #fff;
        height: 4px;
        width: 80%;
        border-radius: 2px;
        position: relative;
    }

    .upload-progress .progress-bar::before {
        content: '';
        position: absolute;
        top: 0;
        left: 0;
        height: 4px;
        width: calc(var(--percent) * 1%);
        background-color: #6b53f3;
        border-radius: 2px;
    }

    .upload-progress .progress-bar::after {
        counter-reset: showProgress var(--percent);
        content: counter(showProgress) '%';
        position: absolute;
        top: -30px;
        left: 50%;
        transform: translateX(-50%);
        color: #fff;
    }

    .upload-result {
        width: 100%;
        position: relative;
    }

    .upload-result button {
        position: absolute;
        right: 0;
    }

    .preview {
        position: absolute;
        z-index: -1;
        width: 100%;
        height: 100%;
        object-fit: contain;
    }

    .select .upload-progress,
    .select .upload-result,
    .select .preview {
        display: none;
    }

    .progress .upload-select,
    .progress .upload-result {
        display: none;
    }

    .result .upload-select,
    .result .upload-progress {
        display: none;
    }
</style>

<script>
    const $ = document.querySelector.bind(document);
    const doms = {
        img:$('.preview'),// 预览图
        container:$('.upload'),// 容器
        select:$('.upload-select'),
        selectFile:$('.upload-select input'),// 选文件
        progress:$('.upload-progress'),// 进度
        cancelBtn:$('.upload-progress button'),// 进度
        delBtn:$('.upload-result button'),// 进度
    }
    function showArea(areaName){
        doms.container.className = `upload ${areaName}`
    }
    function setProgress(value){
        doms.progress.style.setProperty('--perent',value)
    }
    function validateFile(file){
        const sizeLimit = 1* 1024*1024
        const legalExts = ['.jpg','.jpeg','.png','.bmp','.webp','.gif']
        if(file.size>sizeLimit){
            alert('文件大小太大')
            return false;
        }
        const name = file.name.toLowerCase();
        if(!legalExts.some((ext)=>name.endsWith(ext))){
            alert('文件类型不正确')
            return false;
        }
        return true;
    }
    // 原生实现上传函数
    function upload(file, onProgress, onFinish) {
        const xhr = new XMLHttpRequest()
        // 上传完成
        xhr.onload = function () {
            const res = JSON.parse(xhr.responseText)
            onFinish(res)
        }

        // 上传进度
        xhr.upload.onprogress = (e) => {
            const percent = Math.floor((e.loaded / e.total) * 100)
            onProgress(percent)
        }
        xhr.open('POST', 'http://xxxxxxxxx')
        const form = new FormData()
        form.append('avatar', file)
        xhr.send(form)
        // 取消网络请求
        return function () {
            xhr.abort()
        }

    }
    let cancelUpload =null
    function cancel(){
        cancelUpload&&cancelUpload()
        showArea('select')
        doms.selectFile.value=null

    }
    doms.cancelBtn.onclick =doms.delBtn.onclick =cancel;
    doms.select.onclick=function(){
        doms.selectFile.click();
    }
    

    // 拿到文件并上传
    function changeHandler() {
        if (doms.selectFile.files.length === 0) {
            return
        }

        const file = doms.selectFile.files[0]
        if (!validateFile(file)) {
            return
        }

        // 切换界面
        showArea('progress')

        // 显示预览图
        const reader = new FileReader()
        reader.onload = (e) => {
            doms.img.src = e.target.result
        }

        reader.readAsDataURL(file)
        // 上传
        cancelUpload = upload(
            file,
            function (val) {
                // 进度变换了
                setProgress(val)
            },
            function (resp) {
                showArea('result')
            }
        )
    }
    // 拿到选择的文件
    doms.selectFile.onchange = changeHandler

    // 拖拽进入事件
    doms.select.ondragenter = (e) => {
        // 阻止浏览器默认行为
        e.preventDefault()
        doms.select.classList.add('draging')
    }
    
    // 拖拽中
    doms.select.ondragover = (e) => {
        // 阻止浏览器默认行为
        e.preventDefault()
    }

    // 拖拽离开
    doms.select.ondragleave = (e) => {
        doms.select.classList.remove('draging')
    }

    // 拖到div放手后触发
    doms.select.ondrop = (e) => {
        // 阻止浏览器默认行为
        e.preventDefault()

        // 判断被拖动类型
        if (!e.dataTransfer.types.includes('Files')) {
            alert('仅支持拖拽文件')
            return
        }

        // 判断拖拽文件数量
        const files = e.dataTransfer.files
        if (files.length !== 1) {
            alert('仅支持单文件上传')
            return
        }

        doms.select.classList.remove('draging')
        // 直接赋值不能出发input的onchange事件
        doms.selectFile.files = files
        changeHandler()
    }

</script>

</html>